/*
**************************************************************************************************************
File:         interface.sv
Description:  Defines the interface between the DDR controller and the DDR memory stub
Author     :  Rohit Kulkarni
**************************************************************************************************************
*/
`include "package.sv"
/*
//----- DEFINES ----------
parameter DATA_WIDTH  = 72;       //64 bits for data + 8 bits for ECC
parameter ADDR_WIDTH  = 17;       //17 bit address consisting of 14 bit row/column address + 3 bit bank address
parameter BANK_ADDR_WIDTH = 3;    //3 bit bank address
parameter ROW_WIDTH = 7;
parameter COL_WIDTH = 7;
parameter BANK_END = ADDR_WIDTH - BANK_ADDR_WIDTH;   //lowest bit = 14
parameter ROW_END = BANK_END - ROW_WIDTH;  	 //lowest bit = 7
parameter LOW   = 1'b0;
parameter HIGH  = 1'b1;
parameter REFRESH_COUNT_WIDTH = 20;
parameter REFRESH_COUNT_LIMIT = 10'd780; 
*/

/*----- Calculation of refresh count limit ----------
  Refresh interval = 7.8us
  Assuming CK = 100MHz ==> Fck = 0.01us 
  Refresh count limit  = 7.8us / 0.01 us  = 780
*/

/*----- SIGNAL DEFINITIONS -------------

  CK, CK_bar: DIFFERENTIAL CLOCK SIGNALS
  CKE    : CLOCK ENABLE 
  ADDR   : ADDRESS INPUTS
  DQ     : BIDIRECTIONAL DATA (INPUT/OUTPUT)
  CS     : CHIP SELECT
  RAS    : ROW ADDRESS STROBE 
  CAS    : COLUMN ADDRESS STROBE
  WE     : WRITE ENABLE
  DQS    : DATA STROBE
*/

interface DDR_bus(input logic CK, input logic rst);
  wire  [DATA_WIDTH-1:0]  DQ;
  logic  [ADDR_WIDTH-1:0]  addr;
  logic CKE;
  logic RAS;
  logic CAS;
  logic WE;
  logic CS;       
  wire DQS;

/*Non existent signal in the actual DRAM interface, 
but used here in order to allow the stub to output the data when requested by the controller*/
  logic DRAM_d_out;               


  //Modport for the memory controller
 modport memory_controller
  (
    //Import tasks   
    import  enable_chip,
    import  disable_chip,
    import  en_write,
    import  dis_write,
    import  extract_rbc,
    import  activate,
    import  read,
    import  write,
    import  valid_data,
    import  precharge_single_bank,
    import  precharge_all_banks,
    import  refresh,
    import  start_refresh_count,
    import  set_DRAM_data_out,
    import  deassert_DRAM_data_out,
    import toggle_DQ_strobe,
    import assert_DQ_strobe,
    import deassert_pins,
    //Signals for the memory controller  
    input CK,
    inout DQ,
    input rst,
    output addr,
    output CKE,
    output RAS,
    output CAS,
    output WE,
    output CS,
    inout DQS,
    output DRAM_d_out
  );

  //Modport for the memory stub
  modport memory_stub
  (
    //Import tasks
    import toggle_DQ_strobe,
    import assert_DQ_strobe,
    //Signals for the memory stub
    inout DQ,
    input addr,
    input CKE,
    input RAS,
    input CAS,
    input WE,
    input CS,
    inout DQS,
    input DRAM_d_out
  );


  //this should go in a package. do we want to use more commands?
  typedef enum {REF, PRE, PREA, ACT, WR, RD, NOP, DES} commands;

  
  //Internal signals
  logic [REFRESH_COUNT_WIDTH-1:0] refresh_count;
  logic [DATA_WIDTH-1:0] data_sent;
  logic [DATA_WIDTH-1:0] data_received;
  logic DQS_level = HIGH;
  
  assign DQ = (WE ? 'Z : data_sent);
  //assign DQS = (WE ? 'Z : DQS_level);
  
  //assign DQS = DQS_level;
  
  /*
  Task       : assert_DQ_strobe
  Description: asserts the DQS strobe signal during data transfer (data-out cycle)
*/  
  task toggle_DQ_strobe();
   // always_ff @(posedge CK)
    begin
      DQS_level = ~DQS_level;
   end 
  endtask
  
   /*
  Task       : assert_DQ_strobe
  Description: asserts the DQS strobe signal during data transfer (data-out cycle)
*/  
  task assert_DQ_strobe();
   // always_ff @(posedge CK)
    begin
      DQS_level = HIGH;
   end 
  endtask
  
/*
  Task       : en_chip
  Description: Enables the memory stub by deasserting the active low chip select signal
*/  
  task enable_chip();
   // always_ff @(posedge CK)
    begin
      CS  = LOW;
      RAS = HIGH;
      CAS = HIGH;
      WE = HIGH;
      CKE = HIGH;
   end 
  endtask

/*
  Task       : dis_chip
  Description: Disables the memory stub by asserting the active low chip select signal
*/   
  task disable_chip();
  begin
 	  CS = HIGH;
 	  RAS = HIGH;
    CAS = HIGH;
    WE = HIGH;
    CKE = LOW;     
 	end
  endtask
 
/*
  Task       : en_write
  Description: Write enable the DDR stub
*/   
  task en_write();
  begin
 	  WE = LOW;      
 	end
  endtask

/*
  Task       : dis_write
  Description: Write disable the DDR stub
*/   
  task dis_write();
  begin
 	  WE = HIGH;      
 	end
  endtask

/*
  Task       : extract_rbc
  Description: Extract the input logical address into row, bank and column
*/   
  task extract_rbc(input [LOGICAL_ADDR_WIDTH-1:0] ADDR, output [ROW_WIDTH-1:0] row, output [BANK_ADDR_WIDTH-1:0] bank, output [COL_WIDTH-1:0] column);
  begin    
      row    = ADDR[LOGICAL_ADDR_WIDTH-1:ROW_END];
	    bank   = ADDR[ROW_END-1:BANK_END];
    	 column =  ADDR[BANK_END-1:0];
    	 addr = 'z;	
  end    	 
  endtask

/*
  Task       : deassert all pins
  Description: Deassert all pins during idle state
*/  
  task deassert_pins();
	begin
   	  RAS  = HIGH;
	    CAS  = HIGH;
    	 WE   = HIGH;
    	 addr = 'z; 
  end
  endtask
  
/*
  Task       : activate
  Description: Activates the bank indicated by the argument of the task 
               The bank address on the address lines determines the bank to activate 
*/  
  task activate(input [BANK_ADDR_WIDTH-1:0] bank, input [ROW_WIDTH-1:0] row);   //NEED TIMING PARAMETERS
	begin
   	  CS   = LOW;
 	    RAS  = LOW;
	    CAS  = HIGH;
    	 WE   = HIGH;
      addr = {row, bank};  	 
  end
  endtask

/*
  Task       : READ
  Description: Sequence for reading data from the DRAM
*/  
  task read(input [BANK_ADDR_WIDTH-1:0] bank, input [COL_WIDTH-1:0] col);   //NEED TIMING PARAMETERS
	begin
   	  CS   = LOW;
 	    RAS  = HIGH;
	    CAS  = LOW;
    	 WE   = HIGH;
      addr = {bank, col};  	 
  end
  endtask

 /*
  Task       : WRITE
  Description: Sequence for writing data from the DRAM
*/  
  task write(input [BANK_ADDR_WIDTH-1:0] bank, input [COL_WIDTH-1:0] col);   //NEED TIMING PARAMETERS
	begin
   	  CS   = LOW;
 	    RAS  = HIGH;
	    CAS  = LOW;
    	 WE   = HIGH;
      addr = {bank, col};  	 
  end
  endtask
  
/*
  Task       : precharge
  Description: Precharges the row to be used. Precharges after Write operation for open page policy.
*/
  task precharge_single_bank([BANK_ADDR_WIDTH-1:0] bank);   //row passed by reference
  begin
    	CS   = LOW;
    	RAS  = LOW;
    	CAS  = HIGH;
    	WE   = HIGH;
	   addr = bank;
	end
  endtask

/*
  Task       : precharge
  Description: Precharges the row to be used. Precharges after Write operation for open page policy.
*/
  task precharge_all_banks();
  begin    
  	CS   = LOW;
  	RAS  = LOW;
  	CAS  = HIGH;
  	WE   = HIGH;
  	addr = 'z;
	end
  endtask

/*
  Task       : valid_data
  Description: Outputs the data on the data lines in case of a write and inputs the data in case of a read
*/
  task valid_data(input OP, output [DATA_WIDTH-1:0] data_received);

  /*Data to send is just a replication of the address bits. 
    The intention is to generate random data.
    ECC is generated on this random data.    
  */
 
//  logic [DATA_WIDTH-1:0] data_sent;
  
  //assign DQ = (OP ? data_sent : data_received);  
  begin    
  	CS   = LOW;
  	RAS  = HIGH;
  	CAS  = HIGH;
   
   if(OP == 0)      //DDR read
   begin
    WE   = HIGH;    //deassert write enable   
 	  data_received = DQ;    
    
    	  //--call  ECC check here

   end 
   else 
   begin            //DDR write
 	  WE   = LOW;     //assert write enable   
    data_sent = {4{addr}};
    //DQS_level = ~DQS_level;
    //--call  ECC generate here 
   end

	end
  endtask

/*
  Task       : refresh
  Description: Refreshes by reading the data
*/  
  task refresh();
  begin
 	 CS   = LOW;
  	RAS  = LOW;
  	CAS  = LOW;
  	WE   = HIGH;
  	addr = 'z;
 	end
  endtask
  
/*
  Task       : start_refresh_count
  Description: Starts teh refresh count
*/  
  task start_refresh_count();
  begin
    while(refresh_count != REFRESH_COUNT_LIMIT)
    begin
      @(posedge CK)
      refresh_count = refresh_count + 1;
     end 
  end
  endtask
   
/*
  Task       : ECC_compute
  Description: Compute the Error Correcting Code  (Is incomplete)
*/  
  task ECC_compute();
  begin
 	 CS   = LOW;
  	RAS  = LOW;
  	CAS  = LOW;
  	WE   = HIGH;
 	end
  endtask   
   

/*
  Task       : Set DRAM_data_out
  Description: Tell the DRAM stub when to send the data out
*/  
  task set_DRAM_data_out(input logic OP);
  begin
    if(OP == 0)
      DRAM_d_out  = 1;
    else        
      DRAM_d_out  = 0;
 	end
  endtask   

/*
  Task       : Deassert DRAM_data_out
  Description: Tell the DRAM stub not to send the data
*/  
  task deassert_DRAM_data_out();
  begin
    DRAM_d_out  = 0;
 	end
  endtask   

   
endinterface


